﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.EntityFrameworkCore.Internal;
using Nest;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.SQL;
using Wicture.EPC.Common;
using Wicture.QPZS.EPC.Models;
using Wicture.QPZS.EPC.Services;
using Wicture.QPZS.Parts;
using Wicture.QPZS.Parts.Models;

namespace Wicture.QPZS.EPC.Repository
{
    /// <summary>
    /// 保养件查询
    /// </summary>
    public class MaintainRepository : DbRESTFulRepository
    {
        
        public MaintainRepository()
        {
            List<DbRoute> dbConfigs = ConfigurationManager.Settings.GetConfig<List<DbRoute>>("DbRoutes");
            var connectionStr = dbConfigs.FirstOrDefault(t => t.Name == "MaintainPart")?.ReadConnectionString;
            if (string.IsNullOrEmpty(connectionStr))
            {
                throw new LogicalException("maintainpart db config should be configed");
            }
            base.ConnectionManager.ReadConnectionString = connectionStr;
        }

        private readonly string[] supportedBrands = new string[] { "bmw", "mini", "benz"  };
        private readonly string[] supportedBrands2 = new string[] { "skoda", "audi", "seat", "vw", "porsche", "buick", "chevrolet", "cadillac", "bmw", "mini", "rolls_royce", "benz", "maybach", "smart", "jaguar", "landrover", "honda", "acura", "toyota", "lexus" };

        public object QueryMaintainSupportBrand(JToken param)
        {
            return supportedBrands2.Select(q => new { code = q });
        }

        public object GetMaintainList2(JToken param)
        {
            string brandCode = string.Empty;
            try
            {
                var epcRepository = new EpcRepository() { HttpContext = this.HttpContext, CurrentUser = this.CurrentUser };
                var vinInfo = epcRepository.QueryVinFree(param);
                JObject vinInfoJObject = JObject.FromObject(vinInfo);
                var res = vinInfoJObject.Value<int>("resCode");
                if (res != 0)
                {
                    return vinInfo;
                }
                brandCode = vinInfoJObject.Value<string>("brandCode");
                if (!supportedBrands2.Contains(brandCode))
                {
                    string brandName = vinInfoJObject.Value<string>("brandName");
                    return new { resCode = 3, resMessage = $"{brandName}暂不支持保养查询" };
                }
                var selectorId = vinInfoJObject.Value<string>("selectorId");
                var vin = param.Value<string>("vin");
                var data = epcRepository.Call<JArray>(EpcRepository.vinParser.ParseServiceName(brandCode), "Query_Maintain_Parts", new { brandCode, vin, selectorId })?.data;
                if (data != null && data.Any())
                {
                    string model = string.IsNullOrEmpty(vinInfoJObject.Value<string>("fullName")) ? vinInfoJObject.Value<string>("name") : vinInfoJObject.Value<string>("fullName");
                    QueryProcessor.Instance.CheckMaintainCounts(this, vin, brandCode, model);
                    QueryProcessor.Instance.BuildPartListResult(this, data, brandCode);
                    foreach(var item in data)
                    {
                        item["standardImageUrl"] = $"https://cdn.91autoparts.com/icons/maintainparts/{item["standardCode"]}.png";
                    }
                }
                return new
                {
                    VinInfo = vinInfoJObject,
                    MaintainInfo = data,
                };
            }
            catch (LogicalException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To GetMaintainList2 {brandCode}");
                LoggerManager.Logger.LogWithEmail(ex, $"Failed To GetMaintainList2({brandCode})", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }
        public object GetMaintainList(JToken param)
        {
            #region 参数处理

            int distance = param.Value<int>("distance");
            distance = distance / 10000;
            DateTime? time = param.Value<DateTime?>("startTime");
            int month = 0;
            if (time != null)
            {
                TimeSpan ts = DateTime.Now - (DateTime)time;
                month = ts.Days / 30;
            }
            month = month > 0 ? month : 0;

            #endregion
            #region VIN 解析

            var epcRepository = new EpcRepository() { HttpContext = this.HttpContext, CurrentUser = this.CurrentUser };
            var vinInfo = epcRepository.QueryVinFree(param);
            JObject vinInfoJObject = JObject.FromObject(vinInfo);
            var res = vinInfoJObject.Value<int>("resCode");
            if (res != 0)
            {
                return vinInfo;
            }
            Vin vinObj = JsonConvert.DeserializeObject<Vin>(JsonConvert.SerializeObject(vinInfo));
            if (!supportedBrands.Contains(vinObj.brandCode))
            {
                return new { resCode = 3, resMessage = $"{vinObj.brandName}暂不支持保养查询" };
            }
            var brandInfo = epcRepository.QueryBrandInfo(vinObj.brandCode);
            var brandResObj = JObject.FromObject(brandInfo);

            #endregion
            #region 清洁护养
            //List<CleanService> cleans = base.Invoke<CleanService>("QueryCleanService", new { modelId = vinObj.modelId });
            //var cleanDtos = ProcessCleanData(cleans, distance, month);
            #endregion
            #region 易损件
            List<WearingPart> wearing = base.Invoke<WearingPart>("List_Wearing_parts", new { modelId = vinObj.modelId });
            var weearingPartsDto = ProcessWearingpart(wearing, distance, month);
            #endregion
            #region 保养件
            var parts = QueryMaintainPart(vinObj, distance, month);
            var groups = parts.GroupBy(t => t.maintainCode);
            List<MaintainGroupDto> list = new List<MaintainGroupDto>();
            foreach (var group in groups)
            {
                MaintainGroupDto mg = new MaintainGroupDto
                {
                    maintainCode = group.Key,
                    totalPrice = group.First().totalPrice,
                    maintainName = group.First().maintainName
                };
                List<MaintainPartDtoSub> tt = new List<MaintainPartDtoSub>();
                foreach (var item in group)
                {
                    if (!string.IsNullOrEmpty(item.standardCode))
                    {
                        tt.Add(new MaintainPartDtoSub(item));
                    }
                }
                mg.SubItems = tt;
                list.Add(mg);
            }
            #endregion
            return new
            {
                VinInfo = new { vinObj.vin, vinObj.brandCode, vinObj.brandName, vinObj.code, vinObj.props, vinObj.description,vinObj.modelCode, vinObj.imageUrl, vinObj.modelId, vinObj.name, vinObj.selectorId, vinObj.selectorType },
                MaintainInfo = list,
                BrandInfo = brandResObj.Value<object>("data"),
                WearingPartInfo = weearingPartsDto,
                QuerySupport = new
                {
                    distanceSupport = true,
                    monthSupport = vinObj.brandCode != "bmw" && vinObj.brandCode != "mini"
                }
            };
        }
        #region obsolete

        private string LoadZSData(string tmodel)
        {
            try
            {
                HttpClient httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Add("token", ZsTokenDict.Instance.Token);
                return httpClient.GetStringAsync($"http://106.15.89.101:8866/maintenace-parts/model/{tmodel}")
                    .Result;
            }
            catch
            {
                HttpClient httpClient = new HttpClient();
                var res = httpClient.PostAsync(
                        "http://106.15.89.101:8866/maintenace-parts/login?username=user_qpzs&password=B4o5hI992T", null)
                    .Result;
                string strToken = res.Content.ReadAsStringAsync().Result;
                var token = JsonConvert.DeserializeObject<ZsToken>(strToken);
                ZsTokenDict.Instance.Token = token.msg;
                httpClient.DefaultRequestHeaders.Add("token", token.msg);
                return httpClient.GetStringAsync($"http://106.15.89.101:8866/maintenace-parts/model/{tmodel}")
                    .Result;
            }

        }

        private List<CommonPart> ProcessJsonData(string jsonData, int distance = 0, int month = 0)
        {
            var mp = JsonConvert.DeserializeObject<ZsMaintainpart>(jsonData);
            var distinctInfo = mp.maintenanceInfo.Distinct(new PartCompare());
            List<CommonPart> parts = new List<CommonPart>();
            foreach (var item in distinctInfo)
            {
                bool validDistance = int.TryParse(item.maintenanceMileage, out int mainDistance);
                bool validMonth = int.TryParse(item.maintenanceMonth, out int mainMonth);
                var toAdd = BuildFromMaintainInfo(mp, item);
                if (distance > 0 || month > 0)
                {
                    if ((distance > 0 && validDistance && (mainDistance / 2) < distance) ||
                        (month > 0 && validMonth && (mainMonth / 2 < month)))
                    {
                        toAdd.isSelected = true;
                    }
                    else
                    {
                        toAdd.isSelected = false;
                    }
                }
                else
                {
                    toAdd.isSelected = false;
                }

                parts.Add(toAdd);
            }

            return parts;
        }

        private List<CommonPart> ProcessNoVinData(List<NoVinPart> data, string modelId, string brandCode, int distance = 0, int month = 0)
        {
            List<CommonPart> parts = new List<CommonPart>();
            List<string> partCodes = new List<string>();
            foreach (var item in data)
            {
                string partCode = base.Invoke<string>("Get_Maintain_PartCode", new { modelId, brandCode, item.partName });
                if (!string.IsNullOrEmpty(partCode))
                {
                    item.partCode = partCode;
                    partCodes.Add(partCode);
                }
                var toAdd = BuildFromNoVin(item);
                if (distance > 0 || month > 0)
                {
                    if ((distance > 0 && (item.maintainDistance / 2) < distance) ||
                        (month > 0 && (item.maintainSpan / 2 < month)))
                    {
                        toAdd.isSelected = true;
                    }
                    else
                    {
                        toAdd.isSelected = false;
                    }
                }
                else
                {
                    toAdd.isSelected = false;
                }
                parts.Add(toAdd);
            }

            var searchRes = SearchByPartCodes(partCodes.ToArray(), brandCode);
            foreach (var item in parts)
            {
                var esPart = searchRes.Documents.FirstOrDefault(t => t.partCode == item.partCode && t.price > 0);
                item.oesPrice = esPart != null ? $"{esPart.price}" : "-";
            }
            return parts;
        }

        private object ProcessCleanData(List<CleanService> cleans, int distance = 0, int month = 0)
        {
            List<CleanServiceDto> dtos = new List<CleanServiceDto>();
            foreach (var item in cleans)
            {
                var toAdd = new CleanServiceDto()
                {
                    name = item.name,
                    price = item.price,
                    supplier = item.supplier
                };
                if (distance > 0 || month > 0)
                {
                    if ((distance > 0 && (item.maintainDistance / 2) < distance) ||
                        (month > 0 && (item.maintainMonth / 2 < month)))
                    {
                        toAdd.isSelected = true;
                    }
                    else
                    {
                        toAdd.isSelected = false;
                    }
                }
                else
                {
                    toAdd.isSelected = false;
                }
                dtos.Add(toAdd);
            }

            return dtos;
        }
        

        private CommonPart BuildFromMaintainInfo(ZsMaintainpart mp, MaintenanceInfo item)
        {
            CommonPart tt = new CommonPart()
            {
                partName = item.partName,
                partCode = item.partCode,
                type = item.partType,
                oesPrice = item.OESPrice,
                maintainPrice = item.manHourCost
            };

            var brakingAndCoolingOil = mp.brakingAndCoolingOil.FirstOrDefault(t => t.partName == item.partName);
            if (brakingAndCoolingOil != null)
            {
                tt.partCode = brakingAndCoolingOil.partCode;
                tt.oesPrice = brakingAndCoolingOil.oesPrice;
                tt.usage = brakingAndCoolingOil.dosageLiter;
                tt.level = brakingAndCoolingOil.level;
                tt.color = brakingAndCoolingOil.color;
                tt.iceTemperature = brakingAndCoolingOil.iceTemperature;
            }

            var engineTransissionOil = mp.engineTransissionOil.FirstOrDefault(t =>
                t.partName == item.partName || t.transmissionOilPartName == item.partName);
            if (engineTransissionOil != null)
            {
                if (item.partName.Contains("变速箱油"))
                {
                    tt.partCode = engineTransissionOil.partCode;
                    tt.transmissionOilType = engineTransissionOil.transmissionOilType;
                    tt.usage = engineTransissionOil.transmissionOilDosage;
                    tt.transmissionType = engineTransissionOil.transmissionType;
                }
                else
                {
                    tt.oesPrice = engineTransissionOil.manufacturerPrice;
                    tt.usage = engineTransissionOil.dosageLiter;
                    tt.viscosity = engineTransissionOil.viscosity;
                    tt.level = engineTransissionOil.level;
                    tt.composition = engineTransissionOil.composition;
                }
            }
            tt.BuildDescription();
            return tt;
        }

        private CommonPart BuildFromNoVin(NoVinPart item)
        {
            CommonPart tt = new CommonPart()
            {
                partName = item.partName,
                partCode = item.partCode,
                type = item.partType
            };
            return tt;
        }

        private ISearchResponse<Part> SearchByPartCodes(string[] partCodes, string brandCode, int index = 0, int size = 9999)
        {
            using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
            {
                settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName,
                        ElasticSearchConfig.Config.ElasticSearchPassword)
                    .DefaultIndex("accessory")
                    .DefaultMappingFor<Part>(m => m.IndexName("accessory"));
                var client = new ElasticClient(settings);
                var query = new QueryContainer();

                query = new TermsQuery()
                {
                    Field = "partCode",
                    Terms = partCodes

                };

                if (!string.IsNullOrEmpty(brandCode))
                {
                    var brandCodeQuery = new TermQuery()
                    {
                        Field = "brandCode",
                        Value = brandCode
                    };

                    query = query && brandCodeQuery;
                }

                var response = QueryPartsFromES(client, query, index, size);
                return response;
            }
        }

        private ISearchResponse<Part> QueryPartsFromES(ElasticClient client, QueryContainer query, int index, int size)
        {
            return client.Search<Part>(idx => idx
                .Type("part")
                .From(size * index)
                .Size(size).Query(q => query));
        }
        #endregion

        private object ProcessWearingpart(List<WearingPart> parts, int distance = 0, int month = 0)
        {
            var groupedParts = parts.GroupBy(t => t.StandardNameGroup);
            List<WearingPartDto> dtos = new List<WearingPartDto>();
            foreach (var group in groupedParts)
            {
                var toAdd = new WearingPartDto()
                {
                    standardNameGroup = group.Key
                };
                List<WearingPartStd> tt = new List<WearingPartStd>();
                foreach (var part in group.ToList())
                {
                    if (distance > 0 || month > 0)
                    {
                        if ((distance > 0 && (part.MaintainDistance / 2) < distance) ||
                            (month > 0 && (part.MaintainSpan / 2 < month)))
                        {
                            tt.Add(new WearingPartStd()
                            {
                                isSelected = true,
                                standardName = part.StandardName,
                                standardCode = part.StandardCode
                            });
                        }
                        else
                        {
                            tt.Add(new WearingPartStd()
                            {
                                isSelected = false,
                                standardName = part.StandardName,
                                standardCode = part.StandardCode
                            });
                        }

                    }
                    else
                    {
                        tt.Add(new WearingPartStd()
                        {
                            isSelected = false,
                            standardName = part.StandardName,
                            standardCode = part.StandardCode
                        });
                    }
                }
                toAdd.standardPartLists = tt;
                dtos.Add(toAdd);
            }

            return dtos;
        }

        private List<MaintainPartDto> QueryBwmMaintainPart(Vin vin, int distance = 0, int month = 0)
        {
            List<MaintainPartDto> ret = new List<MaintainPartDto>();

            string modelCode = vin.code;
            string sellType = vin.vin.Substring(3, 4);
            string engineCode = vin.props.FirstOrDefault(t => t.key == "发动机型号")?.value;
            string chassisCode = vin.props.FirstOrDefault(t => t.key == "底盘")?.value;
            string transmissionType = vin.props.FirstOrDefault(t => t.key == "变速箱")?.value;
            if (!string.IsNullOrEmpty(transmissionType))
            {
                transmissionType = transmissionType.Substring(0, 1);
            }
            List<MaintainPartEntity> entities = base.Invoke<MaintainPartEntity>("Get_BMW_Maintain", new { modelCode, vin.modelId, sellType, engineCode, chassisCode, transmissionType});
            //var tt = QueryProcessor.Instance.QueryVinParts(this, vin.vin, vin.brandCode);
            foreach (var item in entities)
            {
                var toAdd = new MaintainPartDto(item);
                if (distance > 0)
                {
                    if (distance == item.firstMaintain ||
                        ((distance - item.firstMaintain) % item.circleMaintain == 0))
                    {
                        toAdd.isSelected = true;
                    }
                    else
                    {
                        toAdd.isSelected = false;
                    }
                }
                ret.Add(toAdd);
            }   
            return ret;
        }

        public List<MaintainPartDto> QueryMiniMaintainPart(Vin vin, int distance = 0, int month = 0)
        {
            List<MaintainPartDto> ret = new List<MaintainPartDto>();
            string modelCode = vin.code;
            string sellType = vin.vin.Substring(3, 4);
            string engineCode = vin.props.FirstOrDefault(t => t.key == "发动机型号")?.value;
            //string chassisCode = vin.props.FirstOrDefault(t => t.key == "底盘")?.value;
            string transmissionType = vin.props.FirstOrDefault(t => t.key == "变速箱")?.value;
            if (!string.IsNullOrEmpty(transmissionType))
            {
                transmissionType = transmissionType.Substring(0, 1);
            }
            List<MaintainPartEntity> entities = base.Invoke<MaintainPartEntity>("Get_Mini_Maintain", new { modelCode, vin.modelId, sellType, engineCode, /* chassisCode,*/ transmissionType });
            foreach (var item in entities)
            {
                var toAdd = new MaintainPartDto(item);
                if (distance > 0)
                {
                    if (distance == item.firstMaintain || ((distance - item.firstMaintain) % item.circleMaintain == 0))
                    {
                        toAdd.isSelected = true;
                    }
                    else
                    {
                        toAdd.isSelected = false;
                    }
                }
                ret.Add(toAdd);
            }
            return ret;
        }

        public List<MaintainPartDto> QueryBenzMaintainPart(Vin vin, int distance = 0, int month = 0)
        {
            
            List<MaintainPartDto> ret = new List<MaintainPartDto>();
            // string sellType = vin.vin.Substring(3, 4);
            string modelCode = vin.code;
            string engineCode = vin.props.FirstOrDefault(t => t.key == "发动机")?.value;
            if (!string.IsNullOrEmpty(engineCode))
            {
                engineCode = engineCode.Substring(0, 6).Insert(3, ".");
            }
            string transmissionCode = vin.props.FirstOrDefault(t => t.key == "变速器")?.value;
            if (!string.IsNullOrEmpty(transmissionCode))
            {
                transmissionCode = transmissionCode.Substring(0, 6).Insert(3, ".");
            }

            string priceCode = base.Invoke<string>("Get_Benz_PriceCode_WithBSQ", new { vin.modelId, transmissionCode });
            if (string.IsNullOrEmpty(priceCode))
            {
                priceCode = base.Invoke<string>("Get_Benz_PriceCode_WithoutBSQ", new { vin.modelId });
            }
            List<MaintainPartEntity> entities = base.Invoke<MaintainPartEntity>("Get_Benz_Maintain", new { priceCode, modelCode, vin.modelId, /*sellType,*/ engineCode, transmissionCode });
            foreach (var item in entities)
            {
                var toAdd = new MaintainPartDto(item);
                toAdd.isSelected = false;
                if (distance > 0 && (item.circleMaintain > 0 || item.circleMaintainMonth > 0))
                {
                    if (item.circleMaintain > 0)
                    {
                        if (distance == item.firstMaintain ||
                            ((distance - item.firstMaintain) % item.circleMaintain == 0))
                        {
                            toAdd.isSelected = true;
                        }
                    }

                    if (item.circleMaintainMonth > 0)
                    {
                        if ((month) % item.circleMaintainMonth == 0)
                        {
                            toAdd.isSelected = true;
                        }
                    }
                }
                ret.Add(toAdd);
            }
            return ret;
        }

        public List<MaintainPartDto> QueryMaintainPart(Vin vin, int distance = 0, int month = 0)
        {
            switch (vin.brandCode)
            {
                case "bmw": return QueryBwmMaintainPart(vin, distance, month);
                case "mini": return QueryMiniMaintainPart(vin, distance, month);
                case "benz": return QueryBenzMaintainPart(vin, distance, month);
                default: return new List<MaintainPartDto>();
            }
        }
    }
}
